package mmc.server.shell.ops;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import mmc.server.shell.OperationNotAcceptedException;
import mmc.server.shell.Shell;
import mmc.server.shell.ShellException;
import mmc.server.shell.Util;

public class ClassMemberReference extends Operation implements Assignable{
	
	MMCClass mmcClass;
	ClassMemberReference mmcClassRef;
	MMCClassMember member;
	
	Field field;
	Method method;
	ArgumentList argumentList;
	
    public ClassMemberReference(String text) throws ShellException {
        super(text,".",true,true);
    }

    @Override
	protected Operation parseRValue(String text) throws ShellException{
    	member = new MMCClassMember(text);
    	
    	if(!linkMember()){
        	throw new OperationNotAcceptedException(false, lValue + " has no member "+rValue);
        }
    	
    	return member;
    }
    
    @Override
    public double execute(){
    	if(method!=null){
    		return Util.callMethod(method, lValue.executeComplex(), argumentList);
    	}else{
    		return Util.getField(field, lValue.executeComplex());
    	}
    }
    
    public static PriorityType getPriorityType(){
        return PriorityType.VARIABLE;
    }
    
    public static String getDescription(){
    	return formatHelpString(".","ClassMemberReference");
    }

    public boolean linkMember(){
    	if(member.isMethod()){
    		
    		boolean statiz = (lValue instanceof MMCClass) && ((MMCClass)lValue).isStatic(); 
    		
    		method = Util.searchMethod(lValue.getOutputType(), member.methodName, member.argumentList, statiz);
    		argumentList = member.argumentList;
    		if(method!=null)
    			return true;
    	}else{
	    	for(Field f : lValue.getOutputType().getFields()){
	    		if(f.getName().equalsIgnoreCase(member.text)){
	    			field = f;
	    			return true;
	    		}
	    	}
    	}
    	
    	if(Shell.OP_PARSE_TRACE>0){
    		System.out.println("Linking failed: "+lValue+" . "+member);
    	}
    	
    	return false;
    }
    
    public boolean hasDoubleOutput(){
    	return Util.isAssignableToDouble(getOutputType());
    }
    
    public Object executeComplex(){
    	if(method!=null){
    		return Util.callComplexMethod(method, lValue.executeComplex(), argumentList);
    	}else{
    		return Util.getComplexField(field, lValue.executeComplex());
    	}
    }
    
    public Class<?> getOutputType(){
    	if(method!=null){
    		Class<?> ret = method.getReturnType();
    		
    		if(Shell.OP_PARSE_TRACE>0)
    			System.out.println("OutputType: "+ret);
    		
			return ret;
    	}else{
    		return field.getType();
    	}
    }
    
	@Override
	public void set(Object value) {
		if(method!=null)
    		throw new RuntimeException("Cannot assign "+value+" method "+method);
    	
    	Util.setField(field, value, lValue.executeComplex());
	}
}
